#include "Clock.h"
#include "TDA_Lista.h"
#include "ClockSuscriptor.h"

/* =============================================================================
 * Declaracion Atributos privados
 * =============================================================================
 */


#define GET_INSTANCIA 1
#define DESTRUIR_INSTANCIA 2

struct Clock {
  tLista suscriptores;
  tTiempo tiempo;
};

typedef struct Clock* _tClock;

/*==============================================================================
 * Declaracion de metodos privados
 *==============================================================================
 */

/*
 * Instancia:
 *      Clock se implementa como un singleton.  Instancia, permite operar sobre la 
 * instancia del clock ya sea para crearla o para destruirla.
 * 
 * Pre:
 *      pOpeacion debe especificar "GET_INSTANCIA" o "DESTRUIR_INSTANCIA" según
 * la operacion que se desee llevar a cabo.
 */
static _tClock Instancia (int pOperacion);

static void Notificar (tClock pClock);


/*==============================================================================
 * Definicion de metodos publicos
 *==============================================================================
 */

void clockCrear (tClock* pClock){
  
  if (pClock)
    *pClock = Instancia (GET_INSTANCIA);
    
}

//------------------------------------------------------------------------------

void clockDestruir (tClock pClock){

    if (pClock)
        Instancia (DESTRUIR_INSTANCIA);
}

//------------------------------------------------------------------------------

void clockCorrer (tClock pClock, tTiempo pTiempo){

  if (pClock)
  {
    _tClock clock = (_tClock) pClock;
    clock->tiempo = pTiempo;
    Notificar (pClock);
    clock->tiempo = 0;
  }
}

//------------------------------------------------------------------------------

tTiempo clockGetTiempo (tClock pClock){
    
    tTiempo val = 0;
    
    if (pClock)
        val = ((_tClock) pClock)->tiempo;
    
    return val;
}

//------------------------------------------------------------------------------

void clockSuscribir (tClock pClock, tDato pSuscriptor, tFcnClockNotificar pFcnNotificar){

  if (pClock && pSuscriptor && pFcnNotificar)
  {
    _tClock clock = (_tClock) pClock;
    
    tClockSuscriptor clockSuscriptor;
    clockSuscriptorCrear (&clockSuscriptor, pSuscriptor, pFcnNotificar);

    if (clockSuscriptor)
    {
        
        /* Se agrega el suscriptor unicamente si no se encuentra suscripto
         * de manera tal que solo se encuentre una unica vez en la lista de
         * suscripcion.
         */
        
        int existe = FALSE;
        tIteradorLista iterador = listaObtenerIterador(clock->suscriptores);
        
        while (listaIteradorMoverSiguiente(iterador))
        {
            tClockSuscriptor aux = (tClockSuscriptor) listaIteradorActual(iterador);
            if(clockSuscriptorGetSuscriptor(aux) == pSuscriptor)
                existe = TRUE;
        }
        listaLiberarIterador(iterador);
        
        if (!existe)
            listaAgregarFinal (clock->suscriptores, clockSuscriptor);
    }
    
  }
}

//------------------------------------------------------------------------------

void clockCancelarSuscripcion (tClock pClock, tDato pSuscriptor){

  if (pClock && pSuscriptor)
  {
    tDato suscriptor = listaRemover (((_tClock)pClock)->suscriptores, pSuscriptor);
    clockSuscriptorDestruir(suscriptor);
  }
}

//------------------------------------------------------------------------------

void clockCancelarSuscripciones (tClock pClock){

  if (pClock)
  {
    _tClock clock = (_tClock) pClock;
    
    listaDestruir(clock->suscriptores);
    listaCrear(&(clock->suscriptores), &clockSuscriptorDestruir);
  }
}

/*==============================================================================
 * Definicion de metodos privados.
 *============================================================================*/

static _tClock Instancia (int pOperacion){

  static _tClock clock;
  
  switch (pOperacion)
  {
    case GET_INSTANCIA:
      if (NULL == clock) 
      {
        // Se instancia Clock.
          
        clock = (_tClock) malloc (sizeof(struct Clock));
        if (clock)
          listaCrear (&(clock->suscriptores), &clockSuscriptorDestruir);
      }
      break;
      
    case DESTRUIR_INSTANCIA:
      if (clock)
      {
        if (listaVacia(clock->suscriptores))
        {
          listaDestruir (clock->suscriptores);
          free (clock);
          clock = NULL;
        }
      }
      break;
  }  
  return clock;
}

//------------------------------------------------------------------------------

static void Notificar (tClock pClock){
  
  if (pClock) 
  {
    tIteradorLista iterador = listaObtenerIterador (((_tClock)pClock)->suscriptores);
    while (listaIteradorMoverSiguiente(iterador))
    {
      tClockSuscriptor suscriptor = (tClockSuscriptor) listaIteradorActual(iterador);
      tFcnClockNotificar fcn = clockSuscriptorGetFcnClockNotificar(suscriptor);
      (*fcn) (pClock, clockSuscriptorGetSuscriptor(suscriptor));
    }
    listaLiberarIterador(iterador);
  } 
}
